#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <fstream>
#include <iostream>
#include <string.h>
#include <set>

#include "ui/LaserFinder.h"
#include "ui/Calibrator.h"

#include "io/HMMBuilder.h"
#include "io/BaseSaver.h"
#include "io/BaseLoader.h"
#include "model/HMM.h"
#include "model/MappedRecognizer.impl.h"

#include "parsing/CommandLineParser.h"

#include "test.h"

#include "common.h"

#include "calculator/Calculator.h"
#include "database/TrackBase.h"
#include "dispatcher/LaserDispatcher.h"
#include "Learner.h"
#include "RecognitionTester.h"

#include "crisscross/CrissCross.h"
#include "crisscross/CrissCrossListener.h"

/*
Laser mouse command line

lm calibrate [OPTIONS] <config_file>
lm learn [OPTIONS] <symbol>
lm calc [OPTIONS]
lm cc [OPTIONS]

COMMON OPTIONS
	--help - show this info
	--source=<file> - use <file> as a video source
	--conf=<config_file> - use configuration file <config_file>
	--camid=<number> - use camera #number

ACTION SPECIFIC OPTIONS

1) calibrate
	--manual - manual calibration
	--auto - automatic calibration (default)

2) learn
	--base=<baseDir> - use base directory <baseDir>

3) calc
	--base=<baseDir> - use base directory <baseDir>

4) cc
	--field=<size> - set numer of squares on the field
	--win=<num> - set numer of successively figures for victory
	--base=<baseDir> - use base directory <baseDir>


*/

/*
int main(int argc, const char** argv) {
	
	CommandLineParser parser;
	argv++;
	argc--;
	parser.parse(argc, argv);
	
	std::map<string, string>& optmap = parser.getMap();
	std::set<string>& options = parser.getOptions();
	std::vector<string>& args = parser.getArguments();
	
	string s = optmap[string("camid")];
	int cam_id = 0;
	if (strlen(s.c_str()) != 0) {
		cam_id = atoi(s.c_str());
	}
	
	const char* source = NULL;
	source = optmap[string("source")].c_str();
	if (strlen(source) == 0) {
		source = NULL;
	} else {
		if (!fileExists(source)){
			printf("Source \"%s\" does not exist!\n\n", source);
			return 1;
		}
	}
		
	const char* conf = NULL;
	conf = optmap[string("conf")].c_str();

	LaserFinder lf(NULL, conf, cam_id);
	LaserDispatcher disp(&lf);
	Calculator calc;
	disp.addListener(&calc);
	disp.start();

	return 0;
}
*/

int main(int argc, const char** argv) {
	
	CommandLineParser parser;
	argv++;
	argc--;
	parser.parse(argc, argv);
	
	std::map<string, string>& optmap = parser.getMap();
	std::set<string>& options = parser.getOptions();
	std::vector<string>& args = parser.getArguments();
	
	if (args.size() < 1 || options.find(string("help")) != options.end()) {
		printHelp();
		return 1;
	}
	
	string action = args[0];

	string s = optmap[string("camid")];
	int cam_id = 0;
	if (strlen(s.c_str()) != 0) {
		cam_id = atoi(s.c_str());
	}

	string resX = optmap[string("sizex")];
	int res_x = 640;
	if (strlen(resX.c_str()) != 0) {
		res_x = atoi(resX.c_str());
	}

	string resY = optmap[string("sizey")];
	int res_y = 480;
	if (strlen(resY.c_str()) != 0) {
		res_y = atoi(resY.c_str());
	}

	string baseDir = optmap[string("base")];
	if (baseDir.length() == 0) {
		baseDir = DEFAULT_BASE_DIR;
	} else if (baseDir[baseDir.length() - 1] != '/') {
		baseDir += '/';
	}
	//TrackBase base(baseDir.c_str());

	if (action == string("test")) {
		cout << "test...\n";
		if (args.size() >= 2) {
			test(args[1]);
		} else {
			printf("Too few arguments.\n");
			printHelp();
			return 1;
		}
	} else {
		const char* source = NULL;
		source = optmap[string("source")].c_str();
		if (strlen(source) == 0) {
			source = NULL;
		} else {
			if (!fileExists(source)){
				printf("Source \"%s\" does not exist!\n\n", source);
				return 1;
			}
		}
		
		const char* conf = NULL;
		conf = optmap[string("conf")].c_str();
		
		printf("source = %s\n\n", source);
		printf("action = %s\n\n", action.c_str());
		
		if (action == string("calibrate")) {
			if (strlen(conf) == 0) {
				conf = DEFAULT_CONFIG_FILE;
				if (!fileExists(conf)) {
					printf("Default config file does not exist!... exit\n\n");
					return 1;
				}
			}
			printf("conf = %s\n\n", conf);
			
			bool manual = false;
			if (options.find("manual") != options.end()) {
				manual = true;
				printf("manual calibration...\n");
			} else {
				printf("auto calibration...\n");
			}
			//Calibrator(const char* videoStream, char* configFileName, bool manual);
			Calibrator calibr(source, conf, manual, cam_id);
		} else {
			if (strlen(conf) == 0 || !fileExists(conf)) {
				conf = DEFAULT_CONFIG_FILE;
				if (!fileExists(conf)) {
					printf("Default config file does not exist!... exit\n\n");
					return 1;
				}
			}	
			printf("conf = %s\n\n", conf);
				
			if (action == string("learn")) {
				if (args.size() >= 2) {
					printf("learning...\n");
					LaserFinder lf(source, conf, cam_id, res_x, res_y);
					LaserDispatcher disp(&lf);
					Learner learner(baseDir, args[1]);
					disp.addListener(&learner);
					disp.start();
				} else {
					printf("Too few arguments.\n");
					printHelp();
					return 1;
				}
			} else if (action == string("recognize")) {
				printf("recognition...\n");
				
				LaserFinder lf(source, conf, cam_id, res_x, res_y);
				LaserDispatcher disp(&lf);
				
				RecognitionTester recTester(baseDir);
				
				disp.addListener(&recTester);
				disp.start();
			} else if (action == string("calc")) {
				printf("calc: %d\n", Calculator::evaluate("(3-2)*(3+2)"));
				/*
				LaserFinder lf(source, conf, cam_id, res_x, res_y);
				LaserDispatcher disp(&lf);
				Calculator calc(baseDir);
				disp.addListener(&calc);
				disp.start();
				*/
			} else if (action == string("cc")) {
				string field = optmap[string("field")];
				int field_num = 8;
				if (strlen(field.c_str()) != 0) {
					field_num = atoi(field.c_str());
				}
				string win = optmap[string("win")];
				int win_num = 4;
				if (strlen(win.c_str()) != 0) {
					win_num = atoi(win.c_str());
				}
				int size = std::min(res_x, res_y);
				int textArea = 40;
				LaserFinder lf(source, conf, cam_id, size, size);
				LaserDispatcher disp(&lf);
				CrissCross cc(size, textArea, field_num, win_num);
				CrissCrossListener ccl(baseDir, cc);
				disp.addListener(&ccl);
				cc.show();
				printf("start\n");
				disp.start();
				printf("end\n");
			} else {
				printHelp();
				return 1;
			}
		}
		
	}
	return 0;
}
